home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / CrashTrap.v1.0 / Source / HKCrashTrap.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  9.7 KB  |  404 lines

  1. //
  2. // HKCrashTrap   version 1.0
  3. //
  4. // Based on ObjectError
  5. // Original class by:
  6. // Bill Bumgarner, Andrew Stone, Mike Morton, and Julie Zalenski
  7. //
  8. // Modifications by:
  9. // Ivo Rothschild (ivo@hasc.ca)
  10. // 
  11. // A class that poses as object and does crash-reporting.
  12. // Catches terminating signals (ie seg faults, bus errors)
  13. // and fatal Objective-C runtime errors and writes a backtrace 
  14. // out to the console using some shady hacks.  This could be modified
  15. // to write backtrace to a file, mail message, etc... if desired.
  16. //
  17. //
  18. // You may freely copy, distribute, and reuse the code in this class.
  19. //
  20. // NO WARRANTY:
  21. // ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 
  22. // IS HEREBY DISCLAIMED.  IN NO EVENT WILL THE AFOREMENTIONED PARTIES BE LIABLE 
  23. // FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL 
  24. // DAMAGES ARISING OUT OF THE USE OF OR INABILITY TO USE THIS CODE.
  25. //
  26.  
  27. #import "HKCrashTrap.h"
  28. #import <appkit/Application.h>
  29. #import <appkit/nextstd.h>
  30. #import <appkit/appkit.h>
  31. #import <strings.h>
  32. #import <stdio.h>
  33. #import <sys/signal.h>
  34. #import <objc/objc-runtime.h>
  35.  
  36. typedef struct _sig  {
  37.     int number;
  38.     BOOL isOn;
  39.     char *message;
  40. } SignalItem;
  41.  
  42. typedef struct _type  {
  43.     char encoding;
  44.     char *format;
  45.     char *name;
  46. } ObjcType;
  47.  
  48. extern SignalItem signals[];
  49. extern ObjcType encodings[];
  50.  
  51. static void handle_signal(int signal);
  52. static BOOL ignoreCrashes;
  53. static BOOL continueAfterError;
  54.  
  55. #define FREED_OBJECT_NAME  "<FreedObject>"
  56.  
  57. static void(*_originalError)(Object *anObject, const char *format, va_list ap);
  58. /*
  59. **    Define an error function that dumps a backtrace.
  60. */
  61. static void _objectError(Object *anObject, const char *format, va_list ap)
  62. {
  63.     char buf[BUFSIZ];
  64.  
  65.     vsprintf(buf,format,ap);
  66.     _error = _originalError;
  67.     [HKCrashTrap dumpBacktrace:buf];
  68.     _error = _objectError;
  69.     if (!continueAfterError)  
  70.     {
  71.         abort();
  72.     }
  73.     [HKCrashTrap tryToContinue];
  74.     return;
  75. }
  76.  
  77. @implementation HKCrashTrap
  78.  
  79. + setup
  80. // Initialize everything and poseAs the Object class.
  81. {
  82.     _originalError = _error;
  83.     _error = _objectError;
  84.     
  85.     [[self class] poseAs:[Object class]];
  86.     [[self class] setSignalHandler:handle_signal];
  87.     ignoreCrashes=NO;
  88.     continueAfterError=NO;
  89.     return self;
  90. }
  91.  
  92. + setContinueAfterError:(BOOL)flag;
  93. {
  94.     continueAfterError = flag;
  95.     return self;
  96. }
  97.  
  98. + (BOOL)continueAfterError;
  99. {
  100.     return continueAfterError;
  101. }
  102.  
  103. + setSignalHandler:(void (*)())handler
  104. {
  105.     SignalItem *cur;
  106.     
  107.     for (cur=signals; cur->number; cur++)  {
  108.         if (cur->isOn)  {
  109.             signal(cur->number, handler);
  110.         }
  111.     }
  112.     return self;
  113. }
  114.  
  115. + resumeHandlingCrashes
  116. {
  117.     [[self class] setSignalHandler:handle_signal];
  118.     ignoreCrashes=NO;
  119.     return self;
  120. }
  121.  
  122. + stopHandlingCrashes
  123. {
  124.     [[self class] setSignalHandler:(void (*)())SIG_DFL];
  125.     ignoreCrashes=YES;
  126.     return self;
  127. }
  128.  
  129. //        ASSUMPTION:  The layout of a stack frame for a method invocation is:
  130. //            fp+0 bytes:        calling frame
  131. //            fp+4 bytes:        calling pc
  132. //            fp+8 bytes:        self
  133. //            fp+12 bytes:    selector for method invoked
  134. //            fp+16 bytes:    first argument
  135. //
  136. //        ASSUMPTION: The layout of a stack frame for a function invocation is:
  137. //            fp+0 bytes:        calling frame
  138. //            fp+4 bytes:        calling pc
  139. //            fp+8 bytes:        first argument
  140. //
  141. //        Clearly these are shady assumptions, however we're already
  142. //        in the process of crashing, so what harm can be done?
  143.  
  144. #define MAX_FUNCTION_ARGS  4        // Print at most four args to a function
  145.  
  146. + printFunctionFromFP:(void *)framePointer
  147. {
  148.     char buffer[256];
  149.     char line[1024];
  150.     void *argStart;
  151.     long argNum;    // Index into arguments;
  152.     
  153.     sprintf(line, "function (");
  154.     argStart = framePointer + 8;
  155.     for (argNum=0; argNum<MAX_FUNCTION_ARGS; argNum++)  {
  156.         sprintf(buffer, "%s0x%06lx", ((argNum != 0)?", ":""), 
  157.                     *(((unsigned long *)argStart)+argNum));
  158.         strcat(line, buffer);
  159.     }
  160.     strcat(line, ")");
  161.     NXLogError("%s\n", line);
  162.     
  163.     return self;
  164. }
  165.  
  166. #define IS_CLASS(object)  ([object class] == object)
  167.  
  168. + printMethodFromFP:(void *)framePointer
  169. {
  170.     char buffer[256];
  171.     char line[1024];
  172.     SEL selector;
  173.     id object, class, classIsa;
  174.     Method m;
  175.     BOOL isClassMethod;
  176.     
  177.     object = *(id *)(framePointer+8);  // receiver is 8 bytes from fp
  178.     selector = *(SEL *)(framePointer+12);  // selector is 12 bytes from fp
  179.     
  180.     classIsa = nil;
  181.     object_getInstanceVariable(object, "isa", (void **)&class);
  182.     if (class)
  183.         object_getInstanceVariable(class, "isa", (void **)&classIsa);
  184.     if (!classIsa)
  185.     {
  186.         // Freed object.
  187.         sprintf(line, "%c[%s %s",'?',FREED_OBJECT_NAME, sel_getName(selector));
  188.     }
  189.     else
  190.     {
  191.         isClassMethod = IS_CLASS(object);
  192.     
  193.         sprintf(line, "%c[%s %s", (isClassMethod?'+':'-'), 
  194.                 object_getClassName(object), sel_getName(selector));
  195.     
  196.         m = (isClassMethod    ? class_getClassMethod([object class], selector)
  197.                         : class_getInstanceMethod([object class], selector));
  198.         if (m)  {
  199.             void *argStart = (framePointer+8);
  200.             int argNum, numArgs, offset;
  201.             char *type;
  202.             
  203.             numArgs = method_getNumberOfArguments(m);
  204.             argNum = 2;  // Skip the first two args which are self and _cmd
  205.             while (argNum<numArgs)  {
  206.                 ObjcType *cur;
  207.                 
  208.                 method_getArgumentInfo(m, argNum, &type, &offset);
  209.                 for (cur=encodings; cur->encoding; cur++)  {
  210.                     // Find the ObjcType
  211.                     if (cur->encoding == type[0])  {
  212.                         sprintf(buffer, " :(%s)", cur->name);
  213.                         strcat(line, buffer);
  214.                         sprintf(buffer, cur->format,
  215.                                                 *(long *)(argStart+offset));
  216.                         strcat(line, buffer);
  217.                         break;
  218.                     }
  219.                 }
  220.                 argNum++;
  221.             }
  222.         }  else  {
  223.             strcat(line, " Unknown method");
  224.         }
  225.     }
  226.     
  227.     strcat(line, "]");
  228.     NXLogError("%s\n", line);
  229.     
  230.     return self;
  231. }
  232.  
  233. #define MAX_FRAMES 50
  234.  
  235. + printBacktrace
  236. {
  237.     void *framePointer;                // pointer to current frame
  238.     unsigned int frameCount;        // counter for number of frames printed
  239.     
  240.     [self stopHandlingCrashes];        // Try to avoid re-entry problems
  241.     
  242.     // Start the frame pointer off at our frame
  243.     framePointer = ((void *) &self)-8;
  244.     frameCount=0;
  245.     
  246.     // Assume that a whole lotta frames means either (a) we're trashed or
  247.     // (b) we're in a recursive deathtrap.  In the latter case, we've
  248.     // probably got enough info to see a whole cycle.
  249.     
  250.     while (frameCount<MAX_FRAMES && framePointer)  {
  251.         // If this frame is a method call we'll have a valid 
  252.         // selector at (fp+12).
  253.         if (sel_isMapped(*(SEL *)(framePointer+12)))  {
  254.             [self printMethodFromFP:framePointer];
  255.         }  else  {
  256.             [self printFunctionFromFP:framePointer];
  257.         }
  258.         framePointer = (void *)*(long *)framePointer;  // go up one frame
  259.     }
  260.     return self;
  261. }
  262.  
  263. + dumpBacktrace:(const char *)message
  264. {
  265.     NXLogError("%s\n", message);
  266.     NXLogError("Here's the stack frame Backtrace:\n");
  267.     [self printBacktrace];
  268.     return self;
  269. }
  270.  
  271. static void handle_signal(int signal)
  272. {
  273.     const char *msg=NULL;
  274.     char buf[256];
  275.     SignalItem *cur;
  276.         
  277.     msg = "Unrecognized signal";
  278.     for (cur = signals; cur->number; cur++)  {
  279.         if (cur->number==signal)  {
  280.             msg = cur->message;
  281.             break;
  282.         }
  283.     }
  284.     sprintf(buf, "Caught signal #%d: \"%s\"", signal, msg);
  285.     [HKCrashTrap dumpBacktrace:buf];
  286.     if (!continueAfterError)  {
  287.         exit(1);
  288.     }
  289.     [HKCrashTrap tryToContinue];
  290. }
  291.  
  292. - error:(const char *)aString, ...
  293. {
  294.     va_list ap;
  295.     char buffer[1024];
  296.     
  297.     va_start(ap, aString);
  298.     vsprintf(buffer, aString, ap);
  299.     va_end(ap);
  300.     
  301.     if (!ignoreCrashes)  [[self class] dumpBacktrace:buffer];
  302.     if (continueAfterError)
  303.     {
  304.         [[self class] tryToContinue];
  305.         return self;
  306.     }
  307.     return [super error:buffer];
  308. }
  309.  
  310. + error:(const char *)aString, ...
  311. {
  312.     va_list ap;
  313.     char buffer[1024];
  314.     
  315.     va_start(ap, aString);
  316.     vsprintf(buffer, aString, ap);
  317.     va_end(ap);
  318.     
  319.     if (!ignoreCrashes)  [[self class] dumpBacktrace:buffer];
  320.     if (continueAfterError)
  321.     {
  322.         [self tryToContinue];
  323.         return self;
  324.     }
  325.     return [super error:buffer];
  326. }
  327.  
  328. + tryToContinue;
  329. {
  330.     int mask;
  331.  
  332.     NXLogError("Trying to continue...\n");
  333.     NXRunAlertPanel("Error Alert!","An internal error has occurred.  Try to save work, then quit and restart the application.","Continue",NULL,NULL);
  334.  
  335.     mask = 0;
  336.     sigsetmask(mask);  // unblock all signals...
  337.     [self resumeHandlingCrashes];
  338.  
  339.     [NXApp abortModal];
  340.     return self;
  341. }
  342.  
  343. #define ON        1
  344. #define OFF        0
  345.  
  346. SignalItem signals[] = {
  347.     {SIGHUP,     OFF,     "Hangup"},
  348.     {SIGINT,     OFF,     "Interrupt"},
  349.     {SIGQUIT,     ON,     "Quit"},
  350.     {SIGILL,     ON,     "Illegal instruction"},
  351.     {SIGTRAP,     ON,     "Trace trap"},
  352.     {SIGIOT,     ON,     "IOT instruction"},
  353.     {SIGEMT,     ON,     "EMT instruction"},
  354.     {SIGFPE,     ON,     "Floating point exception"},
  355.     {SIGKILL,     OFF,     "Kill"},
  356.     {SIGBUS,     ON,     "Bus error"},
  357.     {SIGSEGV,     ON,     "Segmentation violation"},
  358.     {SIGSYS,     ON,     "Bad argument to system call"},
  359.     {SIGPIPE,     OFF,     "Write on a pipe with no one to read it"},
  360.     {SIGALRM,     OFF,     "Alarm clock"},
  361.     {SIGTERM,     OFF,     "Software termination"},
  362.     {SIGURG,     OFF,     "Urgent condition present on socket"},
  363.     {SIGSTOP,     OFF,     "Stop"},
  364.     {SIGTSTP,     OFF,     "Stop signal generated from keyboard"},
  365.     {SIGCONT,     OFF,     "Continue after stop"},
  366.     {SIGCHLD,     OFF,     "Child status changed"},
  367.     {SIGTTIN,     OFF,     "Background read attempted from control terminal"},
  368.     {SIGTTOU,     OFF,     "Background write attempted to control terminal"},
  369.     {SIGIO,     OFF,     "I/O is possible on a descriptor"},
  370.     {SIGXCPU,     OFF,     "CPU time limit is exceeded"},
  371.     {SIGXFSZ,     OFF,     "File size limit exceeded"},
  372.     {SIGVTALRM, OFF,     "Virtual timer alarm"},
  373.     {SIGPROF,     OFF,     "Profiling timer alarm"},
  374.     {SIGWINCH,     OFF,     "Window size change"},
  375.     {SIGUSR1,     OFF,     "User defined signal 1"},
  376.     {SIGUSR2,     OFF,     "User defined signal 2"},
  377.     {0},
  378. };
  379.  
  380. ObjcType encodings[] = {
  381.     {_C_ID,            "0x%06lx",        "id"},
  382.     {_C_CLASS,        "0x%06lx",        "Class"},
  383.     {_C_SEL,        "%s",            "SEL"},
  384.     {_C_CHR,        "%c",            "char"},
  385.     {_C_UCHR,        "%c",            "unsigned char"},
  386.     {_C_SHT,        "%s",            "short"},
  387.     {_C_USHT,        "%c",            "unsigned short"},
  388.     {_C_INT,        "%d",            "int"},
  389.     {_C_UINT,        "%d",            "unsigned int"},
  390.     {_C_LNG,        "%l",            "long"},
  391.     {_C_ULNG,        "%l",            "unsigned long"},
  392.     {_C_FLT,        "%f",            "float"},
  393.     {_C_DBL,        "%f",            "double"},
  394.     {_C_VOID,        "0x%06lx",        "void"},
  395.     {_C_PTR,        "0x%06lx",        "ptr"},
  396.     {_C_CHARPTR,    "%s",            "char *"},
  397.     {_C_STRUCT_B,    "%x",            "struct"},
  398.     {0,                "0x%06lx",        "unknown type"},
  399. };
  400.  
  401. @end
  402.  
  403.  
  404.